package com.eclipse.training.models;

import java.io.IOException;
import java.io.InputStream;
import java.util.ArrayList;
import java.util.List;

import org.apache.xerces.parsers.DOMParser;
import org.eclipse.core.resources.IFile;
import org.eclipse.core.runtime.CoreException;
import org.eclipse.ui.IEditorInput;
import org.eclipse.ui.part.FileEditorInput;
import org.jdom2.Document;
import org.jdom2.Element;
import org.jdom2.input.DOMBuilder;
import org.xml.sax.InputSource;
import org.xml.sax.SAXException;

public class ModelProvider {
	public static final String ATT_NAME = "name"; //$NON-NLS-1$
	public static final String ELEM_CONTAINER = "container"; //$NON-NLS-1$
	public static final String ELEM_ELEMENT = "element"; //$NON-NLS-1$

	public static ModelContainer parse(Document xmlModel) {
		Element root = xmlModel.getRootElement();

		ModelContainer container = new ModelContainer(
				root.getAttributeValue(ATT_NAME));

		container.setElements(createElements(root));

		return container;
	}

	private static ArrayList<BaseModelElement> createElements(Element root) {
		ArrayList<BaseModelElement> elements = new ArrayList<BaseModelElement>();

		List<Element> children = root.getChildren();

		for (Element child : children) {
			String nodeName = child.getName();
			if (nodeName.equals(ELEM_CONTAINER)) {
				ModelContainer container = new ModelContainer(
						child.getAttributeValue(ATT_NAME));
				container.setElements(createElements(child));

				elements.add(container);
			} else if (nodeName.equals(ELEM_ELEMENT)) {

				ModelElement element = new ModelElement(
						child.getAttributeValue(ATT_NAME), child.getValue());

				elements.add(element);
			}
		}

		return elements;
	}

	public static ModelContainer update(IFile file) throws CoreException,
			SAXException, IOException {
		Document domModel = doUpdate(file);

		return ModelProvider.parse(domModel);
	}

	public static ModelContainer update(IEditorInput input)
			throws CoreException, SAXException, IOException {
		FileEditorInput fileEditorInput = (FileEditorInput) input;

		IFile file = fileEditorInput.getFile();
		Document domModel = doUpdate(file);

		return ModelProvider.parse(domModel);
	}

	private static Document doUpdate(IFile file) throws CoreException,
			SAXException, IOException {
		InputStream is = file.getContents(true);

		DOMBuilder builder = new DOMBuilder();
		DOMParser parser = new DOMParser();

		parser.parse(new InputSource(is));

		org.w3c.dom.Document doc = parser.getDocument();
		Document domModel = builder.build(doc);
		return domModel;
	}

	public static ModelContainer getModel() {
		ModelContainer root = new ModelContainer("MyModel"); //$NON-NLS-1$

		ArrayList<BaseModelElement> elements = createElement();
		root.setElements(elements);

		return root;
	}

	private static ArrayList<BaseModelElement> createElement() {
		ArrayList<BaseModelElement> container = new ArrayList<BaseModelElement>();

		container.add(createSubContainer());
		container.add(new ModelContainer("Empty container")); //$NON-NLS-1$
		container.add(createSubContainer());
		container.add(new ModelElement("One", "some content for element one")); //$NON-NLS-1$ //$NON-NLS-2$
		container.add(new ModelElement("Two", "some content for element two")); //$NON-NLS-1$ //$NON-NLS-2$
		container.add(new ModelElement("Three", //$NON-NLS-1$
				"some content for element three")); //$NON-NLS-1$
		container
				.add(new ModelElement("Four", "some content for element four")); //$NON-NLS-1$ //$NON-NLS-2$

		return container;
	}

	private static ModelContainer createSubContainer() {
		ModelContainer container = new ModelContainer("First Container"); //$NON-NLS-1$

		container.getElements().add(new ModelContainer("Empty container")); //$NON-NLS-1$
		container.getElements().add(createSubContainer1());
		container.getElements().add(new ModelContainer("Empty container")); //$NON-NLS-1$
		container.getElements().add(new ModelContainer("Empty container")); //$NON-NLS-1$
		container.getElements().add(
				new ModelElement("Element D", "some content for element 'd'")); //$NON-NLS-1$ //$NON-NLS-2$
		container.getElements().add(
				new ModelElement("Element B", "some content for element 'b'")); //$NON-NLS-1$ //$NON-NLS-2$
		container.getElements().add(
				new ModelElement("Element C", "some content for element 'c'")); //$NON-NLS-1$ //$NON-NLS-2$
		container.getElements().add(
				new ModelElement("Element A", "some content for element 'a'")); //$NON-NLS-1$ //$NON-NLS-2$

		return container;
	}

	private static BaseModelElement createSubContainer1() {
		ModelContainer container = new ModelContainer("Second Container"); //$NON-NLS-1$

		container.getElements().add(new ModelContainer("Empty container")); //$NON-NLS-1$
		container.getElements().add(
				new ModelElement("Element X", "some content for element 'x'")); //$NON-NLS-1$ //$NON-NLS-2$
		container.getElements().add(
				new ModelElement("Element Y", "some content for element 'y'")); //$NON-NLS-1$ //$NON-NLS-2$
		container.getElements().add(
				new ModelElement("Element Z", "some content for element 'z'")); //$NON-NLS-1$ //$NON-NLS-2$

		return container;
	}

	public static Document buildDOM(ModelContainer model) {

		Element root = new Element(ELEM_CONTAINER);
		root.setAttribute(ATT_NAME, model.getName());

		root.addContent(buildChildren(model.getElements()));

		return new Document(root);
	}

	private static ArrayList<Element> buildChildren(
			ArrayList<BaseModelElement> elements) {
		ArrayList<Element> children = new ArrayList<Element>();

		for (BaseModelElement element : elements) {
			if (element instanceof ModelContainer) {
				Element container = new Element(ELEM_CONTAINER);
				container.setAttribute(ATT_NAME, element.getName());
				container.addContent(buildChildren(((ModelContainer) element)
						.getElements()));
				children.add(container);
			} else if (element instanceof ModelElement) {
				Element e = new Element(ELEM_ELEMENT);
				e.setAttribute(ATT_NAME, element.getName());
				e.addContent(((ModelElement) element).getContent());
				children.add(e);
			}
		}
		return children;
	}
}
